﻿using DevExpress.XtraPrinting.Preview;
using DevExpress.XtraReports.UI;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using XCI.Core;
using XCI.Helper;
using XCI.Sys.Controls;
using XCI.Sys.Helper;
using XCI.Sys.Model;
using XCI.Sys.Service;
using XCI.Windows;
using XCI.Windows.Extensions;
using XCI.Windows.Helper;

namespace XCI.Sys
{
    /// <summary>
    /// 报表对象
    /// </summary>
    public class ReportObject
    {
        private AsyncWorker asyncWorker = new AsyncWorker();
        private SysReport _report;
        private XtraReportx _devReport;
        private string _filePath;
        private DataSet dataSource;
        private string mode;//当前模式 print:打印模式 design:设计模式 preview:预览模式 view:查看模式

        private ReportObject(SysReport report)
        {
            _report = report;
            if (report == null)
            {
                throw new ApplicationException("无效的报表对象或者无效的报表编码");
            }
        }

        public static ReportObject From(SysReport report)
        {
            ReportService.Instance.SetReportPrinter(report);
            return new ReportObject(report);
        }

        public static ReportObject From(string code)
        {
            SysReport report = ReportService.Instance.SelectByCode(code);
            return From(report);
        }

        public string FilePath
        {
            get
            {
                if (string.IsNullOrEmpty(_filePath))
                {
                    _filePath = GetLocalPath(_report.Code);
                }
                return _filePath;
            }
        }

        public SysReport SysReport => _report;

        public XtraReportx DevReport
        {
            get
            {
                if (_devReport != null) return _devReport;
                LoadReportFile();
                if (File.Exists(FilePath))
                {
                    var r = XtraReport.FromFile(FilePath, true);
                    if (r != null)
                    {
                        var rx = (XtraReportx)r;
                        rx.SetReportObject(this);
                        _devReport = rx;
                    }
                }

                if (_devReport == null)
                {
                    _devReport = new XtraReportx { Font = new Font("宋体", 10f) };
                }
                if (!string.IsNullOrEmpty(_report.Printer))
                {
                    _devReport.PrinterName = _report.Printer;
                }

                IOHelper.CreateDirectoryByPath(FilePath);
                return _devReport;
            }
        }

        private void LoadReportFile()
        {
            if (string.IsNullOrEmpty(_report.Code)) return;
            if (string.IsNullOrEmpty(_report.Path)) return;
            var md5 = string.Empty;
            if (File.Exists(FilePath))
            {
                md5 = StringHelper.Md5String(File.ReadAllText(FilePath, Encoding.UTF8));
            }

            FileUploadService.Instance.Download(_report.Path, FilePath, md5);
        }

        public ReportObject SetDataSource(DataSet ds)
        {
            dataSource = ds;
            return this;
        }

        public ReportObject SetObjectDataSource(object _dataSource)
        {
            DataSet ds = new DataSet { DataSetName = "DataSet" };
            if (_dataSource is IList list)
            {
                var table = DataTableHelper.ConvertListToDataTable(list, "data");
                ds.Tables.Add(table);
            }
            else if (_dataSource is IDictionary dic)
            {
                foreach (string item in dic.Keys)
                {
                    var val = dic[item];
                    DataTable table;
                    if (val is IList dlist)
                    {
                        table = DataTableHelper.ConvertListToDataTable(dlist, item);
                    }
                    else
                    {
                        table = DataTableHelper.ConvertObjectToDataTable(val, item);
                    }
                    ds.Tables.Add(table);
                }
            }
            else
            {
                var table = DataTableHelper.ConvertObjectToDataTable(_dataSource, "data");
                ds.Tables.Add(table);
            }

            dataSource = ds;
            return this;
        }

        public DataSet LoadDataSource(Map map = null)
        {
            return LoadDataSourceCore(_report.Url, map);
        }

        private DataSet LoadDataSourceCore(string url, Map map = null)
        {
            DataSet ds = new DataSet { DataSetName = "DataSet" };
            if (string.IsNullOrEmpty(url)) return ds;

            try
            {
                if (map == null)
                {
                    map = new Map();
                }

                map["mode"] = mode;
                var resultString = SysRuntime.Api.Post(url, map);
                var result = SysRuntime.Api.Deserialize<BoolMessage>(resultString).EnsureSuccess();

                if (result.Data is JArray data)
                {
                    var dt = ToDataTable("data", data);
                    if (dt != null) ds.Tables.Add(dt);
                }
                else
                {
                    JObject jObject = (JObject)result.Data;
                    var propertys = jObject.Properties();
                    if (jObject.First == null)
                    {
                        return ds;
                    }
                    var first = ((JProperty)jObject.First);
                    if (first.Value.Type == JTokenType.Object || first.Value.Type == JTokenType.Array)
                    {
                        foreach (JProperty item in propertys)
                        {
                            if (!item.HasValues) continue;
                            JArray array;
                            if (item.Value is JArray value)
                            {
                                array = value;
                            }
                            else
                            {
                                array = new JArray { JObject.Parse(item.Value.ToString()) };
                            }
                            var dt = ToDataTable(item.Name, array);
                            if (dt != null) ds.Tables.Add(dt);
                        }
                    }
                    else
                    {
                        var dt = ToDataTable("data", new JArray { jObject });
                        if (dt != null) ds.Tables.Add(dt);
                    }
                }
            }
            catch
            {
                MessageBoxHelper.ShowError("加载数据源出错");
            }
            return ds;
        }

        private DataTable ToDataTable(string name, JArray array)
        {
            if (array == null) return null;
            if (!array.HasValues) return null;
            var dt = SysRuntime.Api.Deserialize<DataTable>(array.ToString());
            dt.TableName = name;
            return dt;
        }

        /// <summary>
        /// 设置报表模式
        /// </summary>
        /// <param name="reportMode">报表模式</param>
        public ReportObject SetMode(ReportMode reportMode)
        {
            mode = reportMode.ToString();
            return this;
        }

        public void Print(string printerName = null)
        {
            mode = "print";
            var _ds = dataSource ?? LoadDataSource();
            DevReport.DataSource = _ds;
            DevReport.PrintingSystem.ShowMarginsWarning = false;
            DevReport.PrintingSystem.ShowPrintStatusDialog = false;
            try
            {
                DevReport.Print(printerName);
            }
            catch
            {
                MessageBoxHelper.ShowError("打印报表时出错");
            }
        }

        public void View(DocumentViewer documentViewer, Action completCallback = null)
        {
            mode = "view";
            void create(DataSet dataSet)
            {
                DevReport.DataSource = dataSet;
                documentViewer.PrintingSystem = DevReport.PrintingSystem;
                DevReport.CreateDocument(true);
            }

            if (dataSource == null)
            {
                DataSet _ds = null;
                asyncWorker.RunCallback(p =>
                {
                    _ds = LoadDataSource();
                }).CompletCallback(p =>
                {
                    create(_ds);
                    completCallback?.Invoke();
                }).RunAsync();
            }
            else
            {
                create(dataSource);
                completCallback?.Invoke();
            }
        }

        public void Preview(string title = null)
        {
            mode = "preview";
            var _ds = dataSource ?? LoadDataSource();
            DevReport.DataSource = _ds;
            ReportHelper.Preview(DevReport, title);
        }

        public void Design()
        {
            mode = "design";
            var _ds = dataSource ?? LoadDataSource();
            DevReport.DataSource = _ds;
            ReportHelper.Design(DevReport, FilePath);
            var fileInfo = new FileInfo(FilePath);
            if (fileInfo.Exists)
            {
                var fmodel = FileUploadService.Instance.UploadFile("report", fileInfo.Name, fileInfo.Name, FilePath);
                _report.Md5 = fmodel.Md5;
                _report.Path = fmodel.Url;
                ReportService.Instance.Update(_report);
            }
        }

        #region Private

        /// <summary>
        /// 获取配置文件路径
        /// </summary>
        /// <param name="code">报表编码</param>
        /// <returns>返回指定名称的报表文件路径</returns>
        public static string GetLocalPath(string code)
        {
            return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Report", code + ".repx");
        }

        /// <summary>
        /// 读取文件
        /// </summary>
        /// <param name="code">报表编码</param>
        /// <returns>读取成功返回配置文件内容, 否则返回空字符串</returns>
        private string ReadFile(string code)
        {
            var path = GetLocalPath(code);
            if (File.Exists(path))
            {
                return File.ReadAllText(path, Encoding.UTF8);
            }
            return null;
        }

        /// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="code">报表编码</param>
        /// <param name="setting">配置内容</param>
        private void WriteFile(string code, string setting)
        {
            var path = GetLocalPath(code);
            IOHelper.CreateDirectoryByPath(path);
            File.WriteAllText(path, setting, Encoding.UTF8);
        }

        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="code">报表编码</param>
        private void DeleteFile(string code)
        {
            string path = GetLocalPath(code);
            if (File.Exists(path))
            {
                File.Delete(path);
            }
        }

        #endregion
    }
}